package by.ins.task;

/*
 * Represent task that can be Paused, Continued, and canceled. 
 */

public abstract class ManagedTask extends Task {
	public static final org.apache.log4j.Logger log = org.apache.log4j.LogManager.getLogger(ManagedTask.class);

	private final Object mixer = new Object();

	private long startTime = -1;

	private long finishedTime = -1;

	private TaskCommand taskCommand;
	private final Object taskMixer=  new Object();

	public static abstract class TaskCommand {
		public abstract void execute(ManagedTask task) throws Throwable;
	}

	public static class WaitCommand extends TaskCommand {
		@Override
		public void execute(ManagedTask task) throws Exception {
			synchronized (task.getLocker()) {
				task.status = Status.WAIT;
				task.getLocker().wait();
				task.status = Status.RUNNING;
			}
		}
	}

	public static class PauseCommand extends TaskCommand {
		private long pause;

		public PauseCommand(long pause) {
			this.pause = pause;
		}

		@Override
		public void execute(ManagedTask task) throws Throwable {
			synchronized (task.getLocker()) {
				task.status = Status.PAUSED;
				task.getLocker().wait(pause);
				task.status = Status.RUNNING;
			}
		}
	}

	public static class CancelCommand extends TaskCommand {
		@Override
		public void execute(ManagedTask task) throws Throwable {
			task.status = Status.CANCELED;
			throw new TaskCanceled();
		}
	}

	/*
	 * Should be call notify before execute will be called.
	 */

	public static class ContinueCommand extends TaskCommand {
		/*
		 * public Object getLocker() { return task.getLocker(); }
		 */
		@Override
		public void execute(ManagedTask task) throws Throwable {
			task.status = Status.RUNNING;
		}
	}

	public static class TaskCanceled extends Throwable {
		private static final long serialVersionUID = 1L;

	}

	public Object getLocker() {
		return mixer;
	}

	public ManagedTask(String name, String description) {
		super(name, description);
	}

	public void doTask() {
		if (getStatus() == Status.INACTIVE) {
			startTime = System.currentTimeMillis();
			this.start();
		} else {
			throw new IllegalStateException();
		}
	}

	@Override
	public void run() {
		if (log.isInfoEnabled())
			log.info("Run task : " + this.toString());
		status = Status.RUNNING;
		taskBody();
		status = Status.FINISHED;
	}

	public void pause() {
		if (log.isInfoEnabled())
			log.info("TaskTest is paused : " + this.toString());
		resumeTaskThreadAndPutCommand(new WaitCommand());

	}

	public void pause(long ms) {
		if (log.isInfoEnabled())
			log.info("TaskTest is paused : " + this.toString());
		resumeTaskThreadAndPutCommand(new PauseCommand(ms));

	}

	public void continueT() {
		ContinueCommand cc = new ContinueCommand();
		resumeTaskThreadAndPutCommand(cc);
	}

	public void cancel() {
		resumeTaskThreadAndPutCommand(new CancelCommand());
	}

	protected void resumeTaskThreadAndPutCommand(TaskCommand taskCommand) {

		switch (this.getState()) {
		case WAITING:
		case TIMED_WAITING: {
			// ContinueCommand cc = new ContinueCommand();
			synchronized (getLocker()) {
				getLocker().notifyAll();
			}
		}
		}

		putManagedCommand(taskCommand);

	}

	public void putManagedCommand(TaskCommand taskCommand) {

		// check that previos task are fineshed
		synchronized (taskMixer) {
			if (this.taskCommand != null) {
				throw new IllegalStateException();
			}
			this.taskCommand = taskCommand;
		}

		// resume ThreadWorker
		synchronized (this.getLocker()) {
			this.getLocker().notifyAll();
		}
	}

	protected void checkManagedCommand() throws Throwable {
		TaskCommand tc = null;
		synchronized (taskMixer) {
			if (taskCommand != null) {
				tc = taskCommand;
				taskCommand = null;
			}
		}
		if(tc != null){
			tc.execute(this);
		}
	}

}
